%%%-------------------------------------------------------------------
%%% @author admin
%%% @copyright (C) 2021, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 24. 十二月 2021 11:46
%%%-------------------------------------------------------------------
-module(game_protocol).
-behaviour(gen_server).
-behaviour(ranch_protocol).
-author("admin").

-include("log.hrl").
-include("system_pb.hrl").

-export([
    pack_send/2
]).

%% API
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, start_link/3]).

-record(state,{ref, transport, socket, proto_opt, pack_mod, unpack_mod, decode_mod, encode_mod, key, left_bin, users}).



pack_send(SPid, Data) when is_pid(SPid) ->
    erlang:send(SPid, {send, Data}, [noconnect]),
    ok.
%% --------------------------------------------------------------
start_link(Ref, Transport, ProtocolOptions) ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [Ref, Transport, ProtocolOptions], []).

init([Ref, Transport, ProtocolOptions]) ->
    self() ! init,
    {ok, #state{
        ref = Ref,
        transport = Transport,
        proto_opt = ProtocolOptions,
        pack_mod = pack,
        unpack_mod = unpack,
        encode_mod = encode,
        decode_mod = decode,
        key = "",
        left_bin = <<>>
    }}.

handle_call(_Request, _From, State) ->
    {noreply, State}.

handle_cast(_Request, State) ->
    {noreply, State}.

handle_info(init, State) ->
    NewState = start_init(State),
    {noreply, NewState};
handle_info({tcp, Socket, Data}, State = #state{socket = Socket, transport = Transport}) ->
    case rec_data(Data, State) of
        #state{} = NewState ->
            ok = Transport:setopts(Socket, [{active, once}]),
            {noreply, NewState};
        Err ->
            Transport:close(Socket),
            {stop, Err, State}
    end;
handle_info({send, Data}, State = #state{socket = Socket, transport = Transport}) ->
    case send_data(Data, State) of
        #state{} = NewState ->
            {noreply, NewState};
        Err ->
            Transport:close(Socket),
            {stop, Err, State}
    end;

handle_info({tcp_closed, _}, State = #state{socket = Socket}) ->
    ?INFO("tcp_closed =====> ~w", [Socket]),
    {stop, normal, State};
handle_info(_Info, State) ->
    {noreply, State}.

terminate(Reason, #state{socket = Socket}) ->
    ?INFO("protocol terminate, reason =====> ~w ,socket =====> ~w", [Reason, Socket]),
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.


%% ==============================================

start_init(#state{ref = Ref, transport = Transport} = State) ->
    {ok, Socket} = ranch:handshake(Ref),
    ok = Transport:setopts(Socket, [{active, once}, {packet, 0}]),
    {ok, {_IP_Address, _Port}} = Transport:sockname(Socket),
%%  ?INFO("new connect from =====> ~w:~w", [IP_Address,Port]),
    erlang:put(net_pid, self()),
    pack_send(self(), #s2c_connect{}),
    State#state{socket = Socket}.

rec_data(Data, State = #state{decode_mod = Decode, unpack_mod = Unpack, key = OldKey, left_bin = OldLeft, users = Users}) ->
%%  ?INFO("rec_data =====> ~w", [{Data}]),
    {DecodeBin, Key} = case Decode:decode(Data, OldKey) of
                           {ok, NewBin} -> {NewBin, OldKey};
                           {ok, NewBin, NewKey} -> {NewBin, NewKey}
                       end,
    case Unpack:unpack(<<DecodeBin/binary,OldLeft/binary>>) of
        {ok, PackList, LeftBin} ->
            case net_lib:deal_pack(PackList, Users) of
                {ok ,NewUsers} -> State#state{key = Key, left_bin = LeftBin, users = NewUsers};
                Err -> Err
            end;
        {error, PackList, LeftBin} ->
            case net_lib:deal_pack(PackList, Users) of
                {ok ,NewUsers} -> State#state{key = Key, left_bin = LeftBin, users = NewUsers};
                Err -> Err
            end;
        error -> {error, bad_data}
    end.


send_data(MsgsData, State = #state{encode_mod = Encode, pack_mod = Pack, key = OldKey, transport = Transport, socket = Socket}) ->
    case Pack:pack([MsgsData]) of
        {ok, BinData} ->
            {EncodeBin, Key} = case Encode:encode(BinData, OldKey) of
                                   {ok, NewBinData} -> {NewBinData, OldKey};
                                   {ok, NewBinData, NewKey} -> {NewBinData, NewKey}
                               end,
            case Transport:send(Socket, EncodeBin) of
                ok -> State#state{key = Key};
                Err -> Err
            end;
        error -> {error, bad_data}
    end.
